package pyrachnid.model.ground_model;

import java.util.ArrayList;

import pyrachnid.model.ObservableWithEvent;
import pyrachnid.model.ground_model.Wind.Direction;
import pyrachnid.model.robot_model.Manager;
import pyrachnid.model.robot_model.Robot;
import pyrachnid.model.scenar_model.Scenario;
import Event.Operation.OperationCell;
import Tools.Tuple;

public class Cell extends ObservableWithEvent<OperationCell> implements Cloneable {
	private int _posX;
	private int _posY;
	private int _groundValue;
	private boolean _hasBeenBurned = false;
	private boolean _isReachable = true;
	private boolean _isAllocated = false;
	private Fire _fire;
	private int _nbRobotAllocated;
	
	// Time
	private int _nextUpdate;
	private int _oneTop;
	
	public Cell(int p_positionX, int p_positionY, int p_groundValue){
		_posX = p_positionX;
		_posY = p_positionY;
		_groundValue = p_groundValue;
		_hasBeenBurned = false;
		_isReachable = true;
		_isAllocated = false;
		_fire = null;
		
		// Un top toutes les secondes
		_oneTop = 1000;
		_nextUpdate = _oneTop;
	}
	
	
	@Override
	public Cell clone() throws CloneNotSupportedException {
		Cell clonedCell = (Cell) super.clone();
		if(_fire != null)
			clonedCell._fire = _fire.clone();
		clonedCell._isAllocated = false;
		return clonedCell;
	}


	public int getPositionY() {
		return _posY;
	}
	
	public int getPositionX() {
		return _posX;
	}
	
	public int getGroundValue() {
		return _groundValue;
	}
	
	public boolean getHasbeenBurn() {
		return _hasBeenBurned;
	}

	public void setHasBeenBurn(boolean burn) {
		_hasBeenBurned = burn;
	}
	
	public boolean getIsInFire() {
		return _fire != null;
	}

	public boolean getIsReachable() {
		return _isReachable;
	}

	public void setIsReachable(boolean reach) {
		_isReachable = reach;
	}
	
	public boolean getIsAllocated() {
		return _isAllocated;
	}

	public void setIsAllocated(boolean p_alloc) {
		
		if(p_alloc) this.notifyObserver(this, "Allocated");
		else this.notifyObserver(this, "Not allocate");
		
		_isAllocated = p_alloc;
	}
	

	public Fire getFire() {
		return _fire;
	}

	public void setFire(Fire p_fire) {
		if(_fire != null)
			_fire.detachAll();
		
		_fire = p_fire;
		if(p_fire != null)
		{
			_nextUpdate = _oneTop;
			this.notifyObserver(this, "Fire");
		}
		else
		{
			_fire = null;
			this.notifyObserver(this, "Extinguish Fire");
		}
	}

	public void setNbRobotAllocated(int p_nbRobotAllocated) {
		this.notifyObserver(this, "Allocated");
		this._nbRobotAllocated = p_nbRobotAllocated;
	}

	public int getNbRobotAllocated() {
		return _nbRobotAllocated;
	}
	
	// Distance � vol d'oiseau
	public double getDistanceTo(Cell p_target) {
		double a, b, c;
		
		a = this.getPositionX() - p_target.getPositionX();
		b = this.getPositionY() - p_target.getPositionY();
		
		//c^2 = a^2 + b^2
		c = Math.sqrt(Math.pow(a, 2)+Math.pow(b, 2));
		
		return c;
	}

	public void update(int p_time, Scenario p_scenario) {
		_nextUpdate -= p_time;
		
		if(_nextUpdate <= 0)
		{
			if(_fire != null)
			{
				computeNewWind(p_scenario);
				_fire.incressIntensity(1);
			}

			// next top
			_nextUpdate += _oneTop;
		}
	}
	
	private void computeNewWind(Scenario p_scenario)
	{
		Wind wind = p_scenario.getWind();
		int force = wind.getForce();
		int intencityFire = _fire.getFireIntensity();
		Direction directionWind = wind.getDirection();
		Map map = p_scenario.getMap();

		// new Fire if : fore > 10, value fire > (50+(50-fore/2))
		if(force > 10 && intencityFire > (50 + (50 - force / 2)))
		{
			// Init
			int x = _posX;
			int y = _posY;
			
			// X and Y of the new fire 
			if(directionWind.equals(Direction.NORTH) || directionWind.equals(Direction.NORTH_WEST) || directionWind.equals(Direction.NORTH_EAST)) y -= 1;
			if(directionWind.equals(Direction.SOUTH) || directionWind.equals(Direction.SOUTH_WEST) || directionWind.equals(Direction.SOUTH_EAST)) y += 1;
			if(directionWind.equals(Direction.WEST) || directionWind.equals(Direction.SOUTH_WEST) || directionWind.equals(Direction.NORTH_WEST)) x -= 1;
			if(directionWind.equals(Direction.EAST) || directionWind.equals(Direction.SOUTH_EAST) || directionWind.equals(Direction.NORTH_EAST)) x += 1;
			

			// Cell exist?
			if(x < map.getXdimension() && x >= 0 && y < map.getYdimension() && y >= 0 )
			{
				Cell curentCell = map.getGround()[x][y];
				// Not in fire
				if(!curentCell.getIsInFire())
				{
					curentCell.setFire(new Fire(10));
				}
			}
		}
	}
	
	// Search the robot not allocated the most speed to acces to the cell
	public ArrayList<Tuple<Integer, Robot>> findRobotToFire(Cell p_cell)
	{
		ArrayList<Tuple<Integer, Robot>> result = new ArrayList<Tuple<Integer, Robot>>();
		Manager manager = Manager.getInstance();
		Map map = manager.getMap();
		ArrayList<Robot> robots = manager.getRobot();
		
		for(Robot robot : robots)
		{
			if(!robot.getIsAllocated())
			{	
				double tempsCurent = robot.getTimeToCell(map, p_cell);
				
				genererTupleTimeRobot(result, (int) tempsCurent, robot);
			}
		}
		
		return result;
	}
	
	private void genererTupleTimeRobot(ArrayList<Tuple<Integer, Robot>> tuples, int time, Robot robot)
	{
		boolean find = false;
		Tuple<Integer, Robot> tupleTmp = new Tuple<Integer, Robot>(time, robot);

		for(int i = 0; i < tuples.size() && !find; i++)
		{
			if(time < tuples.get(i).x)
			{
				
				tuples.add(i, tupleTmp);
				find = true;
			}
		}
		
		if(!find) tuples.add(tupleTmp);
	}
}